import type { Waypoint } from '../waypoint'
import { type SegmentItem } from './segment'
import { getEntityMap } from '../base'

export interface WaypointSegmentItem<T> extends SegmentItem<T> {
  id: string
  startWpt: Waypoint
  endWpt: Waypoint
}

export class WaypointSegmentIndex<T> {
  public idx: Record<string, WaypointSegmentItem<T>> = {}

  public segments(): WaypointSegmentItem<T>[] {
    return Object.values(this.idx)
  }

  public add(wpt0: Waypoint, wpt1: Waypoint, t: T) {
    const seg = this.getOrCreate(wpt0, wpt1)
    seg.data.push(t)
  }

  public getById(wpt0: string, wpt1: string): T[] | null {
    const seg = this.getSegment(wpt0, wpt1)
    if (!seg) {
      return null
    }
    return seg.data
  }

  public get(wpt0: Waypoint, wpt1: Waypoint): T[] | null {
    return this.getById(wpt0.id, wpt1.id)
  }

  private getSegment(id0: string, id1: string): WaypointSegmentItem<T> | undefined {
    let id: string
    if (id0.localeCompare(id1) > 0) {
      id = `${id1}@@${id0}`
    } else {
      id = `${id0}@@${id1}`
    }
    return this.idx[id]
  }

  private getOrCreate(wpt0: Waypoint, wpt1: Waypoint): WaypointSegmentItem<T> {
    const { idx: items } = this
    let startWpt = wpt0
    let endWpt = wpt1
    if (wpt0.id.localeCompare(wpt1.id) > 0) {
      startWpt = wpt1
      endWpt = wpt0
    }
    const id = `${startWpt.id}@@${endWpt.id}`
    let item = items[id]
    if (item) {
      return item
    }
    const start = startWpt.location
    const end = endWpt.location
    item = {
      id,
      startWpt,
      endWpt,
      data: [],
      start,
      end,
      minX: Math.min(start.lng, end.lng),
      maxX: Math.max(start.lng, end.lng),
      minY: Math.min(start.lat, end.lat),
      maxY: Math.max(start.lat, end.lat)
    }
    items[id] = item
    return item
  }
}

export function makeRouteSegmentIndex<R extends { waypointList: string[] }>(
  lst: R[]
): WaypointSegmentIndex<R> {
  const ret = new WaypointSegmentIndex<R>()
  const wpts = getEntityMap<Waypoint>('Waypoint')
  for (const r of lst) {
    let wpt0: Waypoint | null = null
    for (const wptId of r.waypointList) {
      const wpt1 = wpts[wptId]
      if (wpt0) {
        ret.add(wpt0, wpt1, r)
      }
      wpt0 = wpt1
    }
  }
  return ret
}
